﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;

namespace DeSleeper.Client.Security
{
	internal enum CredentialSaveOptions
	{
		ForceSave,
		AllowSave,
		DoNotAllowSave
	}

	internal enum CredentialState
	{
		Good,
		Bad,
		UnknownNoSave,
		UnknownSavePending,
		Cancelled
	}

	internal class CredentialsDialog : IDisposable
	{
		private string _host;
		private string _password;
		private string _userName;
		private string _caption;
		private string _message;
		private int _priorAuthorizationErrorCode;

		private bool _useWindowStorage;
		private bool _useIEStorage;
		private CredentialState _storedCredentialsState = CredentialState.Good;
		private CredentialSaveOptions _saveOptions;

		public CredentialState CredentialsState
		{
			get { return _storedCredentialsState; }
		}

		public bool UseWindowStorage
		{
			get { return _useWindowStorage; }
			set { _useWindowStorage = value; }
		}

		public bool UseIEStorage
		{
			get { return _useIEStorage; }
			set { _useIEStorage = value; }
		}

		public CredentialSaveOptions SaveOptions
		{
			get { return _saveOptions; }
			set { _saveOptions = value; }
		}

		public NetworkCredential Credentials
		{
			get
			{
				if (_storedCredentialsState == CredentialState.Bad)
					return null;

				string userPart = "", domainPart = "";
				if (CredentialsPrompt.ParseUserName(_userName, ref userPart, ref domainPart) == CredUIReturnCodes.NO_ERROR)
					return new NetworkCredential(userPart, _password, domainPart);
				else
					return new NetworkCredential(_userName, _password);
			}
		}

		public static CredentialsDialog CreateDialog(Uri host)
		{
			return CreateDialog(host.ToString());
		}

		public static CredentialsDialog CreateDialog(string host)
		{
			return new CredentialsDialog
			             	{
			             		_caption = string.Format("Credentials for {0}", host),
			             		_message = string.Format("{0} requires an authenticated account to perform a wake up.", host),
			             		_useWindowStorage = true,
			             		_saveOptions = CredentialSaveOptions.AllowSave,
			             		_host = host
			             	};
		}


		/// <exception cref="ArgumentOutOfRangeException"><c></c> is out of range.</exception>
		public void GetCredentials(IntPtr hwndParent)
		{
			var uiInfo = new CREDUI_INFO {hwndParent = hwndParent, pszCaptionText = _caption, pszMessageText = _message};

			var flags = CREDUI_FLAGS.SERVER_CREDENTIAL;
			if (!_useIEStorage)
				flags |= CREDUI_FLAGS.GENERIC_CREDENTIALS;
			if (_storedCredentialsState != CredentialState.Good) flags |= CREDUI_FLAGS.ALWAYS_SHOW_UI;
			if (_useWindowStorage)
			{
				switch (_saveOptions)
				{
					case CredentialSaveOptions.ForceSave:
						flags |= CREDUI_FLAGS.PERSIST;
						flags |= CREDUI_FLAGS.EXPECT_CONFIRMATION;
						break;
					case CredentialSaveOptions.AllowSave:
						flags |= CREDUI_FLAGS.EXPECT_CONFIRMATION;
						break;
					case CredentialSaveOptions.DoNotAllowSave:
						flags |= CREDUI_FLAGS.DO_NOT_PERSIST;
						break;
					default:
						throw new ArgumentOutOfRangeException();
				}
			}
			else
			{
				flags |= CREDUI_FLAGS.DO_NOT_PERSIST;
				if (_saveOptions == CredentialSaveOptions.AllowSave)
					flags |= CREDUI_FLAGS.SHOW_SAVE_CHECK_BOX;
			}

			var save = true;
			CredUIReturnCodes retVal = CredentialsPrompt.PromptForCredentials(ref uiInfo, _host, _priorAuthorizationErrorCode, ref _userName, ref _password, ref save, flags);
			switch (retVal)
			{
				case CredUIReturnCodes.NO_ERROR:
					_storedCredentialsState = save ? CredentialState.UnknownSavePending : CredentialState.UnknownNoSave;
					break;
				case CredUIReturnCodes.ERROR_CANCELLED:
					_storedCredentialsState = CredentialState.Cancelled;
					break;
				case CredUIReturnCodes.ERROR_NO_SUCH_LOGON_SESSION:
				case CredUIReturnCodes.ERROR_NOT_FOUND:
				case CredUIReturnCodes.ERROR_INVALID_ACCOUNT_NAME:
				case CredUIReturnCodes.ERROR_INSUFFICIENT_BUFFER:
				case CredUIReturnCodes.ERROR_INVALID_PARAMETER:
				case CredUIReturnCodes.ERROR_INVALID_FLAGS:
					_storedCredentialsState = CredentialState.Bad;
					break;
				default:
					_storedCredentialsState = CredentialState.Bad;
					throw new ArgumentOutOfRangeException();
			}
		}

		public void DeleteCredential()
		{
			CredentialsPrompt.DeleteCredential(_host, _useIEStorage ? CRED_TYPE.DOMAIN_PASSWORD : CRED_TYPE.GENERIC);
		}

		public void ConfirmCredentials()
		{
			_storedCredentialsState = CredentialState.Good;
			CredentialsPrompt.ConfirmCredentials(_host, true);
		}

		///<summary>
		///Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
		///</summary>
		public void Dispose()
		{
			if (_storedCredentialsState == CredentialState.UnknownNoSave || _storedCredentialsState == CredentialState.UnknownSavePending)
			{
				CredentialsPrompt.ConfirmCredentials(_host, false);
				_storedCredentialsState = CredentialState.Bad;
			}
		}
	}
}
